package com.hb.config.web;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Date;

/**
 * spring转换器声明
 *
 * @author zhaojk
 */
@Slf4j
@Configuration
public class HbplatMessageConvertWebMvcConfigurer implements WebMvcConfigurer, WebMvcRegistrations {

    private final HbplatInterceptor interceptor;

    public HbplatMessageConvertWebMvcConfigurer(HbplatInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    /**
     * 后端接收日期型参数时自动转换
     *
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter()); // String转换成java.util.Date
        registry.addConverter(new LocalDateConverter()); // String转换成java.time.LocalDate
    }

    /**
     * 静态资源地址
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 访问路径 -> 映射路径
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        // 注册swagger相关静态资源, 解决 SWAGGER 404报错
        registry.addResourceHandler("/swagger-ui.html", "doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 解决前端js处理大数字丢失精度问题，将Long和BigInteger转换成string
     * 序列化为json对象时, 对于null值和""都不返回
     *
     * @return
     */
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        // 设置转换器
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
                new MappingJackson2HttpMessageConverter();

        // 指定对象和序列化类型
        SimpleModule simpleModule = new SimpleModule();

        // 序列换成json时, 将所有的long变成string 因为js中得数字类型不能包含所有的java long值
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        simpleModule.addSerializer(long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

        simpleModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String dateStr = jsonParser.getText();
                Date date = new DateConverter().convert(dateStr);
                if (date == null) {
                    log.warn("日期格式转换失败: {}", dateStr);
                }
                return date;
            }
        });

        // 转换对象
        ObjectMapper objectMapper = new ObjectMapper();
        // 通过java bean的属性访问控制符和Getter, Setter方法推测哪些属性需要被隐藏和显示
        // 搞得这么复杂玩蛋啊
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

        // 通过该方法对mapper对象进行设置，所有序列化的对象都将按改规则进行系列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 忽略未知的属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 返回前端的json的Date类型统一为时间戳
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);

        // 对带有@JsonFilter("entityFilter")的类的名为 "page", "limit", "from", "offset" 属性屏蔽
        SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        filterProvider.addFilter("entityFilter", SimpleBeanPropertyFilter.serializeAllExcept("page", "limit", "from", "offset"));
        objectMapper.setFilterProvider(filterProvider);

        objectMapper.registerModule(simpleModule);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        return jackson2HttpMessageConverter;
    }

    /**
     * 拦截器相关处理
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器，配置拦截地址
        registry.addInterceptor(interceptor)
                // .addPathPatterns("/**")
                // 不拦截的请求
                .excludePathPatterns(
                        "/user/login", // 登录接口
                        "/valid/code", // 验证码

                        "/v2/**",
                        "/csrf",
                        "/error",
                        "/webjars/**",
                        "/swagger-ui.html",
                        "/swagger-resources/**"
                );
    }
}
